package container;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import server.HttpServlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Mapper {

    private static Server server = new Server();

    //所有的web.xml
    private static Map<String,WebXml> webXmlMap = new HashMap<>();

    //所有需要监听的端口号
    private static Map<String,Integer> ports = new HashMap<>();

    private static Map<String,Host> hostMap = new HashMap<>();

    //将已经匹配过的Servlet缓存起来
    private static Map<String,HttpServlet> cacheServlet = new HashMap<>();

    public static Server getServer() {
        return server;
    }

    public static Map<String, WebXml> getWebXmlMap() {
        return webXmlMap;
    }

    public static Map<String, Integer> getPorts() {
        return ports;
    }

    public static void loadServerAndWebXml(){
        InputStream resourceAsStream = Mapper.class.getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();

        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            Element serverElem = (Element) rootElement.selectNodes("//Server").get(0);
            String serverPort = serverElem.attributeValue("port");
            server.setPort(Integer.valueOf(serverPort));

            List<Element> services = serverElem.selectNodes("//Service");

            for (Element serviceElem : services){
                Service service = new Service();
                String name = serviceElem.attributeValue("name");
                service.setName(name);

                server.getChildren().put(service.getName(),service);

                //设置 Connector
                List<Element> connectors = serviceElem.elements("Connector");
                List<Connector> connectorList = new ArrayList<>(connectors.size());
                service.setConnectorList(connectorList);
                for (Element connectorElem : connectors){
                    String port = connectorElem.attributeValue("port");
                    String connectionTimeout = connectorElem.attributeValue("connectionTimeout");
                    String protocol = connectorElem.attributeValue("protocol");
                    //只处理HTTP协议
                    if (!protocol.toUpperCase().startsWith("HTTP")){
                        continue;
                    }
                    Connector connector = new Connector();
                    connector.setPort(Integer.valueOf(port));
                    connector.setConnectionTimeout(Integer.valueOf(connectionTimeout));
                    connectorList.add(connector);

                    ports.put(port,Integer.valueOf(port));
                }


                //设置engine
                Element engineElem = (Element) serviceElem.elements("Engine").get(0);
                Engine engine = new Engine();
                engine.setName(engineElem.attributeValue("name"));
                engine.setDefaultHost(engineElem.attributeValue("defaultHost"));
                service.setEngine(engine);

                //设置host
                List<Element> hosts = engineElem.elements("Host");
                for (Element hostElem : hosts){
                    Host host = new Host();
                    host.setAppBase(hostElem.attributeValue("appBase"));
                    host.setName(hostElem.attributeValue("name"));
                    engine.getChildren().put(host.getName(),host);
                    //设置端口
                    for (Connector connector : connectorList){
                        host.getPorts().add(connector.getPort());
                    }

                    hostMap.put(generateHostKey(host),host);

                    //设置context
                    List<Element> contexts = hostElem.elements("Context");
                    for (Element contextElem : contexts){
                        Context context = new Context();
                        context.setDocBase(contextElem.attributeValue("docBase"));
                        context.setPath(contextElem.attributeValue("path"));
                        host.getChildren().put(context.getPath(),context);
                    }
                    //加载web.xml
                    loadWebXml(host);

                    //生成Wrapper
                    for (Map.Entry<String,Container> entry : host.getChildren().entrySet()){
                        Context context = (Context) entry.getValue();
                        WebXml webXml = findWebXml(host,context);
                        if (webXml != null){
                            for (Map.Entry<String, ServletEntity> servletEntry : webXml.getServletMap().entrySet()){
                                ServletEntity servletEntity = servletEntry.getValue();
                                //使用反射生成HttpServlet类
                                MyClassLoader myClassLoader = new MyClassLoader(context.getClassPathRoot(host.getAppBase()));
                                Class clazz = myClassLoader.loadClass(servletEntity.getServletClass());
                                HttpServlet httpServlet = (HttpServlet) clazz.newInstance();
                                Wrapper wrapper = new Wrapper();
                                wrapper.setHttpServlet(httpServlet);
                                String urlPattern = servletEntity.getUrlPattern();
                                if (urlPattern.endsWith("/")){
                                    urlPattern = urlPattern.substring(0,urlPattern.length() - 1);
                                }
                                context.getChildren().put(urlPattern,wrapper);
                            }
                        }
                    }
                }
            }


        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static WebXml findWebXml(Host host,Context context){
        String key = generateWebXmlKey(host,context);
        return webXmlMap.get(key);
    }

    public static HttpServlet findServlet(String hostName,Integer port,String url){
        //先在缓存中取，若不存在则进行查找
        String httpServletKey = generateHttpServletKey(hostName,port,url);
        HttpServlet httpServlet = cacheServlet.get(httpServletKey);
        if (httpServlet != null){
            return httpServlet;
        }

        //查找某个host
        String hostKey = generateHostKey(hostName,port);
        Host host = hostMap.get(hostKey);
        if (host == null){
            cacheServlet.put(httpServletKey,null);
            return null;
        }

        //查找某个context
        int length = 0;
        Context context = null;
        Map<String,Container> containerMap = host.getChildren();
        for (Map.Entry<String,Container> entry:containerMap.entrySet()){
            Context context1 = (Context) entry.getValue();
            if (url.contains(context1.getPath())){
                int urlLength = getContextPathLength(context1.getPath());
                if (urlLength > length){
                    length = urlLength;
                    context = context1;
                }
            }
        }

        if (context != null){
            String urlPattern = getUrlPattern(url,context);
            Map<String,Container> wrappers = context.getChildren();
            Wrapper wrapper =  (Wrapper) wrappers.get(urlPattern);
            if (wrapper != null){
                cacheServlet.put(httpServletKey,wrapper.getHttpServlet());
                return wrapper.getHttpServlet();
            }
        }

        cacheServlet.put(httpServletKey,null);
        return null;

    }

    private static String getUrlPattern(String url,Context context){
        String contextPath = context.getPath();
        if (!contextPath.startsWith("/")){
            contextPath += "/" + contextPath;
        }
        if (contextPath.endsWith("/")){
            contextPath = contextPath.substring(0,contextPath.length() - 1);
        }
        if (url.endsWith("/")){
            url = url.substring(0,url.length() - 1);
        }
        return url.substring(contextPath.length());
    }


    private static int getContextPathLength(String contextPath){
        if (contextPath== null || contextPath==""){
            return 0;
        }
        //去掉头尾的"/"
        if (contextPath.startsWith("/")){
            contextPath = contextPath.substring(1);
        }
        if (contextPath.endsWith("/")){
            contextPath = contextPath.substring(0,contextPath.length() - 1);
        }
        return contextPath.split("/").length;
    }

    private static String generateHttpServletKey(String hostName,Integer port,String url){
        String[] parts = url.split("\\?");
        String first = parts[0];
        if (first.endsWith("/")){
            first = first.substring(0,first.length() - 1);
        }
        return hostName + "-" + port + "-" + first;
    }

    private static String generateHostKey(Host host){
        String key = host.getName() + "-" + host.getPorts().get(0);
        return key;
    }

    private static String generateHostKey(String hostName,Integer port){
        String key = hostName + "-" + port;
        return key;
    }


    private static String generateWebXmlKey(Host host,Context context){
        String appBase = host.getAppBase();
        if (!appBase.endsWith(File.separatorChar+"")){
            appBase += File.separatorChar+"";
        }
        String docBase = context.getDocBase();
        if (!docBase.endsWith(File.separatorChar+"")){
            docBase+= File.separatorChar+"";
        }
//        String webXmlPath = appBase + docBase + "WEB-INF/web.xml";
        String webXmlPath = appBase + docBase + "web.xml";
        return webXmlPath;
    }

    //加载应用的web.xml
    private static void loadWebXml(Host host) throws Exception {
        Map<String,Container> containerMap =  host.getChildren();
        for (Map.Entry<String,Container> entry : containerMap.entrySet()){
            Context context = (Context) entry.getValue();
            String webXmlPath = generateWebXmlKey(host,context);
            //已经解析过的不再解析
            if (webXmlMap.containsKey(webXmlPath)){
                continue;
            }
            WebXml webXml = new WebXml();
            webXmlMap.put(webXmlPath,webXml);

            FileInputStream fis = new FileInputStream(webXmlPath);

            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(fis);
            Element rootElement = document.getRootElement();

            List<Element> servlets = rootElement.selectNodes("//servlet");
            List<Element> mappings = rootElement.selectNodes("//servlet-mapping");
            Map<String,String> urlPatternMap = new HashMap<>(mappings.size());
            for (Element mappingElem : mappings){
                String servletName = mappingElem.selectSingleNode("//servlet-name").getText();
                String urlPattern = mappingElem.selectSingleNode("//url-pattern").getText();
                urlPatternMap.put(servletName,urlPattern);
            }
            for (Element servletElem : servlets){
                String servletName = servletElem.selectSingleNode("//servlet-name").getText();
                String servletClass = servletElem.selectSingleNode("//servlet-class").getText();
                String urlPattern = urlPatternMap.get(servletName);
                ServletEntity servletEntity = new ServletEntity();
                servletEntity.setServletName(servletName);
                servletEntity.setServletClass(servletClass);
                servletEntity.setUrlPattern(urlPattern);
                webXml.getServletMap().put(servletName, servletEntity);
            }

        }
    }
}
